#include "Carta.h"
#include "Mazo.h"
#include "Jugador.h"
#include "Fifo.h"
#include "LockFile.h"
#include "Recibidor.h"
#include "Perdedor.h"
#include "Aviso.h"
#include "Logger.h"
#include "Estadistico.h"
#include "FuncionesAuxiliares.h"

#include <iostream>
#include <sstream>
#include <cassert>
#include <string>
#include <cstring>
#include <tuple>
#include <map>
#include <sys/wait.h>

#include "SIGINT_Handler.h"
#include "SignalHandler.h"

#define TAMBUFFER 100

using namespace std;

/* Función auxiliar para imprimir si estuvo OK o no. */
void print_test(string name, bool result)
{
	cout << name << ": " << (result ? "OK" : "ERROR") << endl;
}


void pruebaMazoSinBarajar() {
	
	Mazo mazo = Mazo(10);
	print_test("Mazo NO está vacío", ! mazo.estaVacio() );
	for (int j = 0; j < 4; j++) {
		for (int i = 10; i > 0; i--) {
			Carta tope = mazo.getCarta();
			assert( tope.getNumero() == i);
		}
	}
	print_test("Saca ordenadamente las cartas", true);
	print_test("Ahora el mazo está vacío", mazo.estaVacio() );
}

/*
void pruebaMazoBarajar() {
	Mazo mazo = Mazo(3);
	mazo.barajar();
	for (int i = 0; i < 12; i++) {
		Carta tope = mazo.getCarta();
		cout << "Palo: "<< tope.getPalo() << " Número: " << tope.getNumero() << "\n";
	}
	assert(mazo.estaVacio());
}
*/

int pruebaContarFrecuenciaMap() {
	int vec[] = {1,2,3,4,5,1};
	map<int,int> frec;
	for (int i = 0; i < 6; i++) {
		if (frec[vec[i]])
			frec[vec[i]] += 1;
		else
			frec[vec[i]] = 1;
	}
	//for (map<int,int>::iterator it=frec.begin(); it!=frec.end(); ++it)
	//	cout << it->first << " => " << it->second << '\n';
	cout << frec.begin()->first << "\n";
	return 0;
}


int pruebaJugadorUnaCarta() {
	Jugador p = Jugador();
	p.tomarCarta(Carta(ESPADA,1));
	Carta res = p.dejarCarta();
	assert(res.getPalo() == ESPADA);
	assert(res.getNumero() == 1);
	print_test("Prueba jugador con una carta", true);
	return 0;
}


int pruebaJugadorMultiplesCartas() {
	Jugador p = Jugador();
	p.tomarCarta(Carta(ESPADA,1));
	p.tomarCarta(Carta(ORO,1));
	p.tomarCarta(Carta(BASTO,1));
	p.tomarCarta(Carta(COPA,4));
	Carta res = p.dejarCarta();
	assert(res.getPalo() == COPA);
	assert(res.getNumero() == 4);
	print_test("Prueba jugador con múltiples cartas I", true);

	// Nuevo Jugador
	p = Jugador();
	p.tomarCarta(Carta(ESPADA,1));
	p.tomarCarta(Carta(ORO,1));
	p.tomarCarta(Carta(BASTO,1));
	p.tomarCarta(Carta(COPA,4));
	p.tomarCarta(Carta(ORO,4));
	res = p.dejarCarta();
	assert(res.getNumero() == 4);
	res = p.dejarCarta();
	assert(res.getNumero() == 4);
	res = p.dejarCarta();
	assert(res.getNumero() == 1);
	p.tomarCarta(Carta(BASTO, 3));
	res = p.dejarCarta();
	assert(res.getNumero() == 3);
	print_test("Prueba jugador con múltiples cartas II", true);

	return 0;
}


int pruebaFIFO_1e2r()
{
	int pid = fork();
	
	if(!pid)
	{
		/* Procesos receptores */
		int npid = fork();
		
		if(!npid)
		{
			Fifo canal("canal1.tmp");
		
			char buffer[42];
			int leidos =  canal.leer(buffer, 42);
			buffer[leidos] = '\0';
		
			cout << "soy el proceso " << getpid() << " y lei: " << buffer << endl;
			canal.cerrar();
			exit (0);
		}
		
		else
		{
			Fifo canal("canal1.tmp");
			
			char buffer[42];
			int leidos =  canal.leer(buffer, 42);
			buffer[leidos] = '\0';
			
			cout << "soy el proceso " << getpid() << " y lei: " << buffer << endl;
			canal.cerrar();
			exit (0);
		}
	}
	
	else
	{
		/* Procesos emisores */
		char* uno = (char*) "Primer mensaje";
		char* dos = (char*) "Segundo mensaje";
		
		Fifo canal("canal1.tmp");
		sleep(4);
		canal.escribir(uno, strlen(uno));
		sleep(4);
		canal.escribir(dos, strlen(dos));
		
		wait(NULL);
		canal.cerrar();
		canal.eliminar();
	}

	return(0);

}
	
int pruebaFIFO_2e1r()
{
	int pid = fork();
	
	if(!pid)
	{
		/* Procesos emisores */
		int npid = fork();
		
		if(!npid)
		{
			Fifo canal("canal1.tmp");
			LockFile lock((char*) "canal1.tmp");
			char* mensaje = (char*) "Conmebol 1996";
			
			lock.tomarLock();
			canal.escribir(mensaje, strlen(mensaje));
			cout << "soy el proceso " << getpid() << " y escribo: " << mensaje << endl;
			sleep(2);
			lock.liberarLock();
			
			canal.cerrar();
			exit(0);
		}
		
		else
		{
			Fifo canal("canal1.tmp");
			LockFile lock((char*) "canal1.tmp");
			char* mensaje = (char*) "Apertura 2007";
			
			lock.tomarLock();
			canal.escribir(mensaje, strlen(mensaje));
			cout << "soy el proceso " << getpid() << " y escribo: " << mensaje << endl;
			sleep(2);
			lock.liberarLock();
			
			canal.cerrar();
			exit(0);
		}
	}
	
	else
	{
		/* Proceso receptor */
		Fifo canal("canal1.tmp");
		
		char buffer[42];
		int leidos =  canal.leer(buffer, 42);
		buffer[leidos] = '\0';
		cout << "leo: " << buffer << endl;
		
		leidos =  canal.leer(buffer, 42);
		buffer[leidos] = '\0';
		cout << "leo: " << buffer << endl;
		
		wait(NULL);
		canal.cerrar();
		canal.eliminar();
		exit(0);
	}

}


void pruebaRecibidorYAviso() {
	
	char *fifo_escritura = (char*) "fifo_a_jugador_siguiente_2";
	char *fifo_lectura = (char*) "fifo_recibidor_jugador_2";
	
	int pid = fork();
	
	if (pid == 0) {
		// Hijo y recibidor
		Recibidor reci((char*)"2");
		cout << "HIJO: Arranco a correr" << endl;
		reci.correr();
		cout << "HIJO: terminé de correr" << endl;
		reci.~Recibidor();
		exit(0);
	}
	else {
		Fifo escritura(fifo_escritura);
		Fifo lectura(fifo_lectura);
		char *dato = (char*) "HOLA FIFO!";
		
		sleep(2);
		cout << "Escribo el dato" << endl;
		escritura.escribir(dato, strlen(dato));
		char buffer[100];
		int bytesLeidos = lectura.leer(buffer, 100);
		buffer[bytesLeidos] = '\0';
		
		cout << "Lector: lei el dato [" << buffer << "] del fifo" << endl;
		
		Aviso aviso(FINDELJUEGO);
		int tamBuf;
		char* buffAviso = aviso.serializar(&tamBuf);
		escritura.escribir(buffAviso, tamBuf);
		delete[] buffAviso;
		
		wait(NULL);
		
		escritura.cerrar();
		escritura.eliminar();
		cout << "FIN!" << endl;
	}
}


void pruebaPerdedor() {
	
	char *fifo_escritura = (char*) "a_perdedores";
	char *fifo_lectura = (char*) "a_estadistico";
	
	int pid = fork();
	
	if (pid == 0) {
		// Hijo y perdedor
		Perdedor per((char*)"2");
		cout << "HIJO: Arranco a correr" << endl;
		per.correr();
		cout << "HIJO: terminé de correr" << endl;
		per.~Perdedor();
		exit(0);
	}
	else {
		Fifo escritura(fifo_escritura);
		Fifo lectura(fifo_lectura);
		
		char buffer[100];
		Aviso ronda(FINDERONDA);
		int tamBuf;
		char* buffAviso = ronda.serializar(&tamBuf);
			
		for (int i = 0; i < 3; i++) {
			sleep(3);
			
			cout << "Escribo el dato" << endl;
			escritura.escribir(buffAviso, tamBuf);
			
			int bytesLeidos = lectura.leer(buffer, 100);
			buffer[bytesLeidos] = '\0';
			
			cout << "Lector: lei el dato [" << buffer << "] del fifo" << endl;
		}
		
		delete[] buffAviso;
		
		Aviso fin(FINDELJUEGO);
		buffAviso = fin.serializar(&tamBuf);
		escritura.escribir(buffAviso, tamBuf);
		delete[] buffAviso;
		
		wait(NULL);
		
		escritura.cerrar();
		escritura.eliminar();
		cout << "FIN!" << endl;
	}
}


const char* intACString(int num)
{
	/* (mística C++) */
	string str;
	stringstream sal;
	sal << num;
	str = sal.str();
	return str.c_str();
}


/* DEPRECATED: NO SIRVE MAS */	
/* Prueba la interacción simple entre procesos. */
int pruebaProcesos(int n_jugadores) {
	/*	Almaceno los pid de los jugadores.	*/
	
	/*	int jugadores[n_jugadores] <-- c se queja!
		src/main.cpp:355: error: ISO C++ forbids variable length array ‘jugadores’ */
	int* jugadores = new int[n_jugadores];
	
	/*	Creo JC	*/
	jugadores[0] = fork();
	if(jugadores[0] == 0)
	{
		execlp((char*) "./procJugadorCoordinador", (char*) "procJugadorCoordinador", (char*) NULL);
	}
	
	/* Convierto la cantidad de jugadores para pasarlo como opción
	* al proceso Jugador. */
	const char* n_jugadores_cstr = intACString(n_jugadores);
	
	/*	Creo jugadores.	*/
	for(int i = 1; i < n_jugadores; i++)
	{
		jugadores[i] = fork();
		
		if(jugadores[i] == 0)
		{
			const char* n_jugador_cstr = intACString(i + 1);
			execlp((char*) "./procJugador", (char*) "procJugador", n_jugador_cstr, n_jugadores_cstr, (char*) NULL);
		}
		
	}
	
	/*	Espero a que todos los jugadores terminen.	
	for(int i = 0; i < n_jugadores; i++)
		wait(NULL); //pid(jugadores[i], (int*) NULL, 0);
	*/
	return 0;
	
}


/* DEPRECATED: NO SIRVE MAS */
/* esta prueba funciona con el recibidor forwardeando el aviso de fin de juego
 * puede que deje de funcionar mas adelante, ya que ese comportamiento no es correcto.*/
int pruebaPasoCarta() {
	// creo un proceso jugador
	
	char* fifo_escritura = (char*) "fifo_a_jugador_siguiente_1";
	char* fifo_lectura = (char*) "fifo_recibidor_jugador_1";
	
	char *jugador = (char*) "1", *jugadoresTotal = (char*) "1";
	
	int pid = fork();
	
	if (pid == 0) { 
		execlp((char*) "./procJugador", (char*) "procJugador", jugador, jugadoresTotal, (char*) NULL);
	}
	
	Fifo escritura(fifo_escritura);
	Fifo lectura(fifo_lectura);
	
	char bufferLectura[100];
	
	for (int i = 1; i < 4; i++) {
		sleep(3);
		
		Carta carta(BASTO, i);
		int tamBuff;
		char* buffCarta = carta.serializar(&tamBuff);
		cout << "PPAL: Escribo el dato " << i << endl;
		escritura.escribir(buffCarta, tamBuff);
		
		delete[] buffCarta;
		
		int bytesLeidos = lectura.leer(bufferLectura, 100);
		
		Carta aux(0,0);
		memcpy(&aux, bufferLectura, bytesLeidos);
		
		cout << "PPAL: lei carta palo: " << aux.getPalo() << " numero: "<< aux.getNumero() << endl;
	}
	
	Aviso fin(FINDELJUEGO);
	int tamBuf;
	char* buffAviso = fin.serializar(&tamBuf);
	escritura.escribir(buffAviso, tamBuf);
	delete[] buffAviso;
	
	wait(NULL);
	
	lectura.cerrar();
	escritura.cerrar();
	escritura.eliminar();
	cout << "FIN!" << endl;
	return 0;
}


void pruebaSerializacion() {
	Aviso av(GANO);
	
	cout << "AvisoViejo tipo:"<<av.getTipo()<<endl;
	int tam;
	char *buff = av.serializar(&tam);
	Aviso nuevo = Aviso::deserializar(buff,tam);
	cout << "Aviso tipo:"<<nuevo.getTipo()<<endl;
	delete[] buff;
	
	Carta ca(3,3);
	buff = ca.serializar(&tam);
	Carta nu = Carta::deserializar(buff, tam);
	cout << nu <<endl;
	delete[] buff;
}


///* DEPRECATED: NO SIRVE MAS creo...*/
///*	Prueba una ronda de dos jugadores. */
//int pruebaJCRepartir()
//{
	//char *jugadorUno = (char*) "1", *jugadorDos = (char*) "2",
		 //*jugadoresTotal = (char*) "2";
		 
	//char* fifo_escritura_j1 = (char*) "fifo_coord_a_jug_1";
	//char* fifo_lectura_j1 = (char*) "fifo_jug_a_coord_1";
	//char* fifo_escritura_j2 = (char*) "fifo_coord_a_jug_2";
	//char* fifo_lectura_j2 = (char*) "fifo_jug_a_coord_2";
	
	//Fifo escritura_j1(fifo_escritura_j1);
	//Fifo lectura_j1(fifo_lectura_j1);
	//Fifo escritura_j2(fifo_escritura_j2);
	//Fifo lectura_j2(fifo_lectura_j2);
	
	//int jugadores[2];
	
	///* loop unrolling... para mejor performance en el caché (?) */
	//jugadores[0] = fork();
	//if(jugadores[0] == 0)
	//{
		//execlp((char*) "./procJugador", (char*) "procJugador", jugadorUno, jugadoresTotal, (char*) NULL);
	//}
		
	//jugadores[1] = fork();
	//if(jugadores[1] == 0)
	//{
		//execlp((char*) "./procJugador", (char*) "procJugador", jugadorDos, jugadoresTotal, (char*) NULL);
	//}	
	
	///* reparto	*/
	//Mazo mazo = Mazo(2);
	//mazo.barajar();
	
	//// Reparte cartas
	//for (int i = 0; i < (2 * 4); i++) {
		//Carta tope = mazo.getCarta();
		
		//int tamBuff;
		//char* buffCarta = tope.serializar(&tamBuff);
		//char buffACK[TAMBUFFER];
		
		//if(i % 2 == 0)
		//{
			//escritura_j1.escribir(buffCarta, tamBuff);
			
			//int bytesLeidos = lectura_j1.leer(buffACK, TAMBUFFER);
			//Aviso ack = Aviso::deserializar(buffACK, bytesLeidos);
			//if (ack.getTipo() != OK)
			//{
				//cout << "TODO manejar error." << endl;
				//cout << "kb" << endl;
				//return 1;
			//}
		//}
		
		//else
		//{
			//escritura_j2.escribir(buffCarta, tamBuff);

			//int bytesLeidos = lectura_j2.leer(buffACK, TAMBUFFER);
			//Aviso ack = Aviso::deserializar(buffACK, bytesLeidos);
			//if (ack.getTipo() != OK)
			//{
				//cout << "TODO manejar error." << endl;
				//cout << "kb" << endl;
				//return 1;
			//}			
			
		//}
		
		//delete[] buffCarta;		
	//}
	
	//int ganoAlguien = 0;
	
	//// Mando el ACK inicial
	//int tamBuf;
	//Aviso ack(OK);
	//char* buffAv = ack.serializar(&tamBuf);
	//escritura_j1.escribir(buffAv, tamBuf);
	//escritura_j2.escribir(buffAv, tamBuf);
	//cout << "mandé ACK inicial" << endl;
	//delete[] buffAv;

	
	///*	Empieza el juego. */
	//while (! ganoAlguien) {
		//// Espero que me avise que termino la mano: veo si alguien gano.
		//sleep(1);
		//char buffAviso[TAMBUFFER];
		//int leido1 = lectura_j1.leer(buffAviso, TAMBUFFER);
		//Aviso j1 = Aviso::deserializar(buffAviso, leido1);	
		//int leido2 = lectura_j2.leer(buffAviso, TAMBUFFER);
		//Aviso j2 = Aviso::deserializar(buffAviso, leido2);
		
		//cout << "Aviso j1: "<<j1.getTipo()<<" Aviso j2: "<< j2.getTipo()<<endl;
		
		//if (j1.getTipo() == GANO || j2.getTipo() == GANO) {
			//ganoAlguien = 1;
			//Aviso victoria(GANO);
			//buffAv = victoria.serializar(&tamBuf);
			//escritura_j1.escribir(buffAv, tamBuf);
			//escritura_j2.escribir(buffAv, tamBuf);
			//cout << "AVisé que alguien gano" << endl;
			//delete[] buffAv;
		//} else {
			//// Manda sincronismo
			//Aviso sinc(OK);
			//buffAv = sinc.serializar(&tamBuf);
			//escritura_j1.escribir(buffAv, tamBuf);
			//escritura_j2.escribir(buffAv, tamBuf);
			//cout << "Mandé sincronismo" << endl;
			//delete[] buffAv;
		//}
	//}
	
	//wait(NULL);
	//wait(NULL);
	
	//escritura_j1.cerrar();
	//lectura_j1.cerrar();
	//escritura_j2.cerrar();
	//lectura_j2.cerrar();
	
	//escritura_j1.eliminar();
	//escritura_j2.eliminar();
	
	//return 0;
//}


int pruebaLogger() {
	int pid = fork();
	if (pid == 0) {
		Logger::activarDebug();
		Logger::loggear("seria genial que esto funcione","hijo");
		Logger::loggear("do","hijo");
		return 0;
	}
	Logger::loggear("seria genial que esto funcione","padre");
	Logger::loggear("ah si?","padre");
	wait(NULL);
	return 0;
}

int pruebaRondaCoordinador(int cantJugadores) {
	
	char* jugadores[] = {(char*)"2",(char*)"3",(char*)"4",(char*)"5",(char*)"6",(char*)"7",(char*) "8"};
	
	// creo coord
	int pid = fork();
	if(pid == 0) {
		execlp((char*) "./procJugadorCoordinador", (char*) "procJugadorCoordinador", jugadores[cantJugadores - 2], (char*) NULL);
	}
	
	
	for (int i = 0; i <= cantJugadores - 2; i++) {
		pid = fork();
		if(pid == 0){
			execlp((char*) "./procJugador", (char*) "procJugador", jugadores[i], jugadores[cantJugadores - 2], (char*) NULL);
		}
	}
		
	for (int i = 0; i < cantJugadores; i++)
		wait(NULL);
	
	return 0;
}

void mostrarPuntuaciones()
{
	cout << "Acá se mostarían las puntuaciones" << endl;
}

int pruebaPuntuaciones()
{
	SIGINT_Handler sigint_handler;
	SignalHandler* sh = SignalHandler :: getInstance();
	sh->registrarHandler ( SIGINT,&sigint_handler );
	
	cout << "Empieza el programa, ctrol - c para ver las puntuaciones." << endl;
	cout << "(Detener con ctrol - z)" << endl;
	
	for(int i = 0; i < 15; i++)
	{
		if(sigint_handler.getGracefulQuit() != 0)
		{
			mostrarPuntuaciones();
			sigint_handler.restaurarEstado();
		} 
		cout << "Iteración " << i << " de 15." << endl;
		sleep(5);		
	}
	
	return 0;
}



int pruebaPuntajes(int cantJugadores) {
	
	map<string, int> puntaje = map<string,int>();
	for (int i = 1; i <= cantJugadores; i++) {
		puntaje[ itos(i) ] = 0;
		cout << "Clave:"<<itos(i)<<endl;
	}
	
	int perdedor = 1;
	
	string nombrePerdedor = itos(perdedor);
	puntaje[nombrePerdedor] += 1;
	
	string chancho[] = { "C", "H", "A", "N", "C", "H", "O" };
	for (map<string,int>::iterator it = puntaje.begin(); it != puntaje.end(); it++) {
		int cantPerdidas = it->second;
		string pal("");
		for (int i = 0; i < cantPerdidas; i++) pal += chancho[i];
		cout << "Jugador " << it->first <<": "<< pal << endl;
	}
	return 0;
}

int main() {
	return 0;
}
